package model.trader.portfolio;

import java.util.ArrayList;
import java.util.List;

import model.jgap.PortfolioController;
import model.market.BankOfCredit;
import model.market.MarketConfig;
import model.market.MarketInformation;
import model.market.MarketManager;
import model.market.Position;
import model.trader.TradeInstruction;
import model.util.SharpeCalc;
import model.util.stats.RegressionCalc.RegressionResult;

//AMLOW:  test this!
public class EvolvingPortfolioManager implements PortfolioManagementStrategy{

	PortfolioController controller = new PortfolioController();
	BankOfCredit bank = new BankOfCredit();
	double realWorth;
	
	public TradeInstruction[] allocate(Position pos, MarketManager market,
			double[] analystPrices, MarketInformation marketInfo) {
		double[] realMarketPrices = marketInfo.getMarketPrices();
		realWorth = pos.getWorth(realMarketPrices);
		int[] desiredTradeNumbers = evolveTradingIntention(pos, 
				analystPrices, marketInfo, market.getConfig(),marketInfo.getMarketPrices(),null);
		
		TradeInstruction[] trades = new TradeInstruction[marketInfo.getNumberOfAssets()];
		
		
		
		//AM: guesses
		double[] guesses = new double[marketInfo.getNumberOfAssets()];
		for(int asset=0;asset<marketInfo.getNumberOfAssets();asset++){
			guesses[asset]=pos.getExecutedQuantity(asset)*marketInfo.getMarketPrices()[asset];
		}
		
		for(int i=0;i<trades.length;i++){
			TradeInstruction trade=new TradeInstruction();
			trade.setNumberToTrade(desiredTradeNumbers[i]);
			trade.setAsset(i);
			trades[i]=trade;
			
			double[] marketClone = new double[realMarketPrices.length];
			System.arraycopy(realMarketPrices, 0, marketClone, 0, realMarketPrices.length);
			int desiredTradeQuantity = trade.getNumberToTrade();
			marketClone[i]=realMarketPrices[i];
			double holdPrice = marketClone[i];
			//Just a check to see if we are performing initial portfolio population..
			if(market.getPeriod()!=market.getConfig().getTradeDelay()){
				if(trade.getNumberToTrade()>0){
					do{
	
						holdPrice*=1.011;
						desiredTradeQuantity = evolve(pos, analystPrices,
								marketInfo, i, marketClone, holdPrice,guesses, market.getConfig());
						trade.addIndication(holdPrice, desiredTradeQuantity);
	
						if(holdPrice>10*realMarketPrices[i]){
							
							System.out.println("We got an endless loop... "+holdPrice+
									" "+realMarketPrices[i]+" "+pos.getExecutedQuantity(i)+" "+desiredTradeQuantity+" "+analystPrices[i]);
//							if(true){
//								System.out.println("Exception ");
//								trade.draw(realMarketPrices[i]);
//								throw new RuntimeException("We got an endless loop... Hold: "+holdPrice+
//									"  Market: "+realMarketPrices[i]+" We got:"+pos.getExecutedQuantity(i)+" We want to buy:"+desiredTradeQuantity+" Analyst says:"+analystPrices[i]+" div "+marketInfo.getAssetCashflows()[i].get(marketInfo.getAssetCashflows()[i].size()-1));
//							}
							
							break;
						}
					}while(desiredTradeQuantity>0);
					
				}else{
					//AM: same same but different..
					do{
						holdPrice/=1.011;
						desiredTradeQuantity = evolve(pos, analystPrices,
								marketInfo,  i,marketClone, holdPrice,guesses, market.getConfig());
						trade.addIndication(holdPrice, desiredTradeQuantity);
						if(holdPrice<realMarketPrices[i]/4){
							System.out.println("We got an endless loop sell... "+holdPrice+
									" "+realMarketPrices[i]+" "+pos.getExecutedQuantity(i)+" "+desiredTradeQuantity);
							break;
						}
					}while(desiredTradeQuantity<0);
					
				}
				if(i==0){
					trade.draw(realMarketPrices[i]);
				}
			}
			
			
		}
		
		return trades;
	}

	
	private int evolve(Position pos, double[] analystPrices,
			MarketInformation marketInfo,  int i,
			double[] marketClone, double holdPrice,double[] guesses,MarketConfig config) {
		marketClone[i]=holdPrice;
		return evolveTradingIntention(pos,  analystPrices, marketInfo,config,marketClone,guesses)[i];
		
	}

	private int[] evolveTradingIntention(Position pos,
			 double[] analystPrices,
			MarketInformation marketInfo,
			MarketConfig config,
			double[] marketPrices, double[] guesses) {
		
		double[] alpha = calculateAlphas(analystPrices, marketInfo, marketPrices);

		double excessIndexCf=0;
		double anticipatedNewIndexPrice = 0;
		
		//AMLOW:
		// - get the beta over more than one period working nicely.
		// - do the work here to get a decent handle on major up-tick in asset.

		/////////////////////////////////////////////////AM: start code that needs thorough checking.
		
		double[] expectedExcessReturns = new double[marketInfo
				.getNumberOfAssets()];
		for (int i = 0; i < marketInfo.getNumberOfAssets(); i++) {
			double beta = marketInfo.getBeta(i);
			// if(alpha[i]>0){
			// "+marketPrices[i]);
			// }
			double expectedExcessReturn = marketInfo.getEquityRiskPremium()
					* beta + alpha[i];
			expectedExcessReturns[i] = expectedExcessReturn;
			excessIndexCf+=expectedExcessReturn*marketPrices[i];
			anticipatedNewIndexPrice+=marketPrices[i];
		}

		
		List<Double> indexPrices = new ArrayList<Double>(marketInfo.getIndexReturnsInclCashAndCapitalLessRiskFree());
		indexPrices.add(excessIndexCf/anticipatedNewIndexPrice);
		
		double indexVariance = marketInfo.runVariance(indexPrices)*marketInfo.getConfig().getPeriodsInYear();
		
		//AM: like the index variance calc this one is important. the test is to take it out and give an asset a big jump in value,
		//		you'll see a spike in value followed by a drop as vol increases.. 
		List<Double>[] assetReturnsInclCashAndCapitalLessRiskFree = marketInfo.getAssetReturnsInclCashAndCapitalLessRiskFree();
		
		double[] assetNonSpecVariance = new double[marketPrices.length];
		
		for(int i=0;i<marketInfo.getNumberOfAssets();i++){
			List<Double> assetReturns = new ArrayList<Double>( assetReturnsInclCashAndCapitalLessRiskFree[i]);
			assetReturns.add(expectedExcessReturns[i]);
			double assetVariance = marketInfo.runVariance(assetReturns)*marketInfo.getConfig().getPeriodsInYear();
			//AM: this looks supicious.. we build imaginary marketindex with only this asset modified.. of course beta will approach one right?
			RegressionResult runCapm = marketInfo.runCapm(assetReturns.size(),assetReturns, indexPrices);
			
			double assetNonSpecificVariance = marketInfo.getAssetNonSpecificVariance(runCapm.getBeta(), assetVariance, indexVariance);
			
			assetNonSpecVariance[i]=assetNonSpecificVariance;
			
		}
		////////////////////////////////////////////////////AM: end code that needs thorough checking.
		
		
		int[] ret = new int[marketInfo.getNumberOfAssets()];
		try {
			// AM: Note that the manipulated market prices arent being fed in..
			// only smaller alphas.
			double[] balancePortfolioWeightings = controller.balancePortfolio(
					marketInfo, expectedExcessReturns, guesses,indexVariance,assetNonSpecVariance);

			// AM: convert to asset holdings..
			//AM: note we use the actual worth of the investor right now.. because otherwise when leveraged they will go crazy on selling.
			double worth = realWorth;//pos.getWorth(marketPrices);
			double availableLongPos = bank.getPossibleLongPosition(worth,.2);

			double portfolio;
			String appetite = config.getAppetite();
			if (appetite.equals(MarketConfig.APPETITE_FIXED)) {
				portfolio = worth;
			} else if (appetite.equals(MarketConfig.APPETITE_STD_DEV)) {
				// target 10% std dev...
				double vol = SharpeCalc.calculatePortfolioStdDev(
						balancePortfolioWeightings, expectedExcessReturns,
						marketInfo,indexVariance,assetNonSpecVariance);
				if (vol == 0) {
					vol = .01;
				}
				portfolio = worth * (.1 / vol);
			} else {// proportional to market sharpe or fixed sharpe ratio...
				double compareSharpe = 1;

				double calculateSharpeRatio = SharpeCalc.calculateSharpeRatio(
						balancePortfolioWeightings, expectedExcessReturns,
						marketInfo,indexVariance,assetNonSpecVariance);

				double marketSharpeMultiplier = config.getSharpeMultiplier().multiplier;
				double leverage = (calculateSharpeRatio/compareSharpe)*marketSharpeMultiplier;
				
				portfolio = worth * leverage;

//AMLOW: is interesting to think about.. maybe put it back one day.	if (appetite.equals(MarketConfig.APPETITE_FIXED_SHARPE_BINARY)) {
//					if (leverage > 1) {
//						portfolio = availableLongPos;
//					} else {
//						portfolio = 0;
//					}
//				}
			}

			if (portfolio < 0) {
				portfolio = 0;
			}
			if (portfolio > (availableLongPos)) {
				portfolio = availableLongPos;
			}
			
			for (int i = 0; i < balancePortfolioWeightings.length; i++) {
				int desiredNumberHolding = (int) (balancePortfolioWeightings[i]
						* portfolio / marketPrices[i]);
				int currentHolding = pos.getExecutedQuantity(i);
				if (currentHolding < 0) {
					// AM: negative holdings are handled elsewhere.
					currentHolding = 0;
				}
				ret[i] = desiredNumberHolding - currentHolding;
				// AM: note if current holding negative then we must always buy
				// something....handled in demand curve trade structurer.
			}
			
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return ret;
	}

	private double[] calculateAlphas(double[] analystPrices, MarketInformation marketInfo, double[] marketPrices) {
		// Need:
		double[] alpha = new double[analystPrices.length];
		// here we assume that the difference between the analyst price and the
		// fair price will produce
		// the risk free rate. Then we calculate that cashflow, divide it by the
		// market price to find the alpha.
		for (int i = 0; i < analystPrices.length; i++) {
			double difference = analystPrices[i] - marketPrices[i];
			double rfCashflow = difference
					* marketInfo.getRiskFreeReturns().get(
							marketInfo.getRiskFreeReturns().size() - 1);
			alpha[i] = rfCashflow / marketPrices[i];

		}
		return alpha;
	}

}
